home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / adduser / adduser.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-16  |  29.8 KB  |  1,232 lines

  1. /* 
  2.  * adduser.c --
  3.  *
  4.  *    Add a user to /etc/passwd, create a home directory,
  5.  *      create a .project file, add them to the sprite-users
  6.  *      mailing list, and send them mail to inform them that
  7.  *      their account is ready.
  8.  *
  9.  * Copyright 1990 Regents of the University of California
  10.  * Permission to use, copy, modify, and distribute this
  11.  * software and its documentation for any purpose and without
  12.  * fee is hereby granted, provided that the above copyright
  13.  * notice appear in all copies.  The University of California
  14.  * makes no representations about the suitability of this
  15.  * software for any purpose.  It is provided "as is" without
  16.  * express or implied warranty.
  17.  */
  18.  
  19. #ifndef lint
  20. static char rcsid[] = "$Header: /sprite/src/admin/adduser/RCS/adduser.c,v 1.13 91/12/16 12:12:40 kupfer Exp $";
  21. #endif /* not lint */
  22.  
  23. #include "common.h"
  24. #include <sprite.h>
  25. #include <assert.h>
  26. #include <bstring.h>
  27. #include <ctype.h>
  28. #include <errno.h>
  29. #include <fcntl.h>
  30. #include <grp.h>
  31. #include <signal.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <sys/file.h>
  36. #include <sys/ioctl.h>
  37. #include <sys/param.h>
  38. #include <sys/stat.h>
  39. #include <sys/wait.h>
  40. #include <unistd.h>
  41.  
  42. /* This is where we keep a prototype home directory. */
  43. #define NEWUSER_DIR    "/user1/newuser"
  44.  
  45. /* This is the host that has the master UID database. */
  46. #define DATABASE_HOST    "thalm"
  47.  
  48. #if 0
  49. /* XXX MAIL_TMP doesn't allow for concurrent addusers */
  50. #define MAIL_TMP    "/tmp/mail.adduser"
  51.  
  52. #define MAIL_MSG    mail_message
  53.  
  54. static CONST char mail_message[] = "\
  55. Your Sprite account is ready.\n\
  56. If you have any problems or questions\n\
  57. let me know.\n\n\
  58. Your passwd is < enter passwd >\n";
  59. #endif /* 0 */
  60.  
  61. static char whoami[0x100];
  62.  
  63. /* Forward references: */
  64. static void add2MailingList _ARGS_((CONST char *list, CONST char *name));
  65. static void cleanup _ARGS_((int sig));
  66. static int  copy _ARGS_((CONST char *from, CONST char *to, int mode));
  67. static void createHomeDirectory _ARGS_((CONST char *dir, CONST char *username,
  68.            CONST char *project, CONST char *forward, int uid, int gid));
  69. static char *getHomeDir _ARGS_((void));
  70. static char *getPasswdEntryFromDataBase _ARGS_((void));
  71. static char *getPasswdEntryInteractive _ARGS_((CONST struct passwd *pwd));
  72. static char *getPasswdEntryFromUser _ARGS_((void));
  73. static void insertPasswdEntry _ARGS_((CONST struct passwd *newpw));
  74. static void makeLinktoHomeDir _ARGS_((CONST char *homedir,
  75.                       CONST char *username));
  76. static int  parsePasswdEntry _ARGS_((char *p, struct passwd *ps));
  77. static void printPasswdEntry _ARGS_((CONST struct passwd *pwd,
  78.                      CONST char *dir));
  79. static int  sed _ARGS_((CONST char *filename, CONST char *expr, int mode));
  80. static char *xmalloc _ARGS_((int n));
  81.  
  82. /*
  83.  * Flags to keep track of what the program has done, so we can back out
  84.  * semi-gracefully if the program is interrupted.
  85.  */
  86. enum Step { NOT_DONE_YET, DONE, SKIP_THIS_STEP };
  87. static enum Step create_ptmp_file      = NOT_DONE_YET;
  88. static enum Step move_ptmp_to_passwd   = NOT_DONE_YET;
  89. static enum Step create_home_dir       = NOT_DONE_YET;
  90. static enum Step edit_mailaliases_file = NOT_DONE_YET;
  91. static enum Step make_symlink          = NOT_DONE_YET;
  92. static char real_homedir[MAXPATHLEN];
  93. static char sym_homedir[MAXPATHLEN];
  94. static char project[BUFFER_SIZE];
  95. static char forward[BUFFER_SIZE];
  96.  
  97.  
  98. /*
  99.  *----------------------------------------------------------------------
  100.  *
  101.  * main --
  102.  *
  103.  *    Get account information from the user, or from the ucb database.
  104.  *      Use the information to create an account.
  105.  *
  106.  * Results:
  107.  *    0 exit status if no errors.  Non-zero if there were
  108.  *      problems.
  109.  *
  110.  * Side effects:
  111.  *    Account is created.  If the program is interrupted, it is
  112.  *      supposed to be able to back out with out changing anything.
  113.  *
  114.  *----------------------------------------------------------------------
  115.  */
  116.  
  117. void
  118. main(argc, argv)
  119.     int argc;
  120.     char **argv;
  121. {
  122.     struct passwd passwdStruct;
  123.     int c;
  124.     char *entry;
  125.     char *dir;
  126.     char *username;
  127.     struct passwd *pwd;
  128.     char from[MAXPATHLEN], to[MAXPATHLEN], *tend, *fend;
  129.     int fd;
  130.  
  131.     if ((pwd = getpwuid(getuid())) == NULL) {
  132.     fprintf(stderr, "getpwuid failed: %s\n", strerror(errno));
  133.     exit(EXIT_FAILURE);
  134.     }
  135.     strcpy(whoami, pwd->pw_name);
  136. #ifndef TEST
  137.     /* 
  138.      * Do any necessary security checks and setup.
  139.      */
  140.     SecurityCheck();
  141. #endif
  142.     signal(SIGINT, cleanup);
  143.     printf(
  144.      "Enter 1 if you already have a /etc/passwd file from another machine.\n");
  145.     printf("Enter 2 if you want to fetch an entry from the ucb data base.\n");
  146.     printf("Enter 3 if you want to enter the information interactively.\n");
  147.     printf("Enter q to quit\n");
  148.     for (;;) {
  149.     printf("Please choose 1, 2 or 3: ");
  150.     c = raw_getchar();
  151.     printf("\n");
  152.     switch(c) {
  153.     case '1':  entry = getPasswdEntryFromUser();        break;
  154.     case '2':  entry = getPasswdEntryFromDataBase();    break;
  155.     case '3':  entry = getPasswdEntryInteractive(NULL);    break;
  156.     case 'q':  case 'Q':    exit(EXIT_FAILURE);
  157.     default:   continue;
  158.     }
  159.     break;
  160.     }
  161.     for (;;) {
  162.     while (parsePasswdEntry(entry, &passwdStruct) == 0) {
  163.         fprintf(stderr, "Entry is mangled, please correct it.\n");
  164.         c = '1';
  165.         entry = getPasswdEntryInteractive(&passwdStruct);
  166.     }
  167.     getString("", "Project    ", project);
  168.     for (;;) {
  169.         getString("", "Forward    ", forward);
  170.         if (*forward && strchr(forward, '@') == NULL) {
  171.         fprintf(stderr,
  172.            "Improperly formed address, should be ``user@machine''\n");
  173.         continue;
  174.         }
  175.         break;
  176.     }
  177.     dir = getHomeDir();
  178.     printPasswdEntry(&passwdStruct, dir);
  179.     if (strcmp(passwdStruct.pw_passwd,"*")==0) {
  180.         fprintf(stderr,"WARNING: passwd is '*' - login disabled\n");
  181.     }
  182.     if (yes("Is this correct?")) {
  183.         break;
  184.     }
  185.     entry = getPasswdEntryInteractive(&passwdStruct);
  186.     }
  187.     sprintf(real_homedir, "%s/%s", dir, passwdStruct.pw_name);
  188.     username = passwdStruct.pw_name;
  189.     insertPasswdEntry(&passwdStruct);
  190.     makeLinktoHomeDir(real_homedir, username);
  191.     if (create_ptmp_file == DONE) {
  192.  
  193.         if (makedb(PTMP_FILE)) {
  194.         (void)fprintf(stderr, "adduser: mkpasswd failed.\n");
  195.         cleanup(0);
  196.         }
  197.  
  198.         /*
  199.          * possible race; have to rename four files, and someone could slip
  200.          * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
  201.          * so that getpwent(3) can't slip in; the lock should never fail and
  202.          * it's unclear what to do if it does.  Rename ``ptmp'' last so that
  203.          * passwd/vipw/chpass can't slip in.
  204.          */
  205.         fend = strcpy(from, PTMP_FILE) + strlen(PTMP_FILE);
  206.         tend = strcpy(to, PASSWD_FILE) + strlen(PASSWD_FILE);
  207.         bcopy(".dir", fend, 5);
  208.         bcopy(".dir", tend, 5);
  209.         if ((fd = open(from, O_RDONLY, 0)) >= 0) {
  210.         (void)flock(fd, LOCK_EX);
  211.     }
  212.         /* here we go... */
  213.         (void)rename(from, to);
  214.         bcopy(".pag", fend, 5);
  215.         bcopy(".pag", tend, 5);
  216.         (void)rename(from, to);
  217.         bcopy(".orig", fend, 6);
  218.         (void)rename(PASSWD_FILE, PASSWD_BAK);
  219.         (void)rename(MASTER_PASSWD_FILE, MASTER_BAK);
  220.         (void)rename(from, PASSWD_FILE);
  221.         (void)rename(PTMP_FILE, MASTER_PASSWD_FILE);
  222.         /* done! */
  223.  
  224.     move_ptmp_to_passwd = DONE;
  225.     }
  226.     createHomeDirectory(real_homedir, username, project, forward,
  227.     passwdStruct.pw_uid, passwdStruct.pw_gid);
  228.     add2MailingList("sprite-users", username);
  229. #if 0    
  230.     if (yes("do you want to send mail?")) {
  231.     sendmsg(username);
  232.     }
  233. #endif    
  234.     printf("Done creating account for %s\n", username);
  235.     exit(EXIT_SUCCESS);
  236. }
  237.  
  238.  
  239. /*
  240.  *----------------------------------------------------------------------
  241.  *
  242.  * parseGecos --
  243.  *
  244.  *    Parse the gecos field of a passwd entry.
  245.  *
  246.  * Results:
  247.  *    Returns the next comma-sperated field.
  248.  *
  249.  * Side effects:
  250.  *    Overwrites the commas with null bytes.
  251.  *
  252.  *----------------------------------------------------------------------
  253.  */
  254.  
  255. static char *
  256. parseGecos(p)
  257.     char *p;
  258. {
  259.  
  260.     while (*p) {
  261.     if (*p++ == ',') {
  262.         p[-1] = '\0';
  263.         return p;
  264.     }
  265.     }
  266.     return p;
  267. }
  268.  
  269.  
  270. /*
  271.  *----------------------------------------------------------------------
  272.  *
  273.  * printPasswdEntry --
  274.  *
  275.  *    Pick apart a passwd entry and print it out.
  276.  *
  277.  * Results:
  278.  *    None.
  279.  *
  280.  * Side effects:
  281.  *    Prints the passwd information to stdout.
  282.  *
  283.  *----------------------------------------------------------------------
  284.  */
  285. static void
  286. printPasswdEntry(pwd, dir)
  287.     CONST struct passwd *pwd;
  288.     CONST char *dir;
  289. {
  290.     CONST struct group *gwd;
  291.     char *group, *fullname, *office, *phone, *home_phone;
  292.     char *gecos;
  293.  
  294.     if ((gwd = getgrgid(pwd->pw_gid)) == NULL) {
  295.     group = "Invalid Group";
  296.     } else {
  297.     group = gwd->gr_name;
  298.     }
  299.     gecos = xmalloc(strlen(pwd->pw_gecos));
  300.     strcpy(gecos, pwd->pw_gecos);
  301.     fullname = gecos;
  302.     office = parseGecos(fullname);
  303.     phone = parseGecos(office);
  304.     home_phone = parseGecos(phone);
  305.     printf("\n");
  306.     printf("Login name: %s\n", pwd->pw_name);
  307.     printf("Full name:  %s\n", fullname);
  308.     printf("Passwd:     %s\n", pwd->pw_passwd);
  309.     printf("Uid:        %d\n", pwd->pw_uid);
  310.     printf("Group:      %s\n", group);
  311.     printf("Office:     %s\n", office);
  312.     printf("Phone       %s\n", phone);
  313.     printf("Home Phone: %s\n", home_phone);
  314.     printf("Directory:  %s -> %s/%s\n", pwd->pw_dir, dir, pwd->pw_name);
  315.     printf("Shell:      %s\n", pwd->pw_shell);
  316.     printf("Project:    %s\n", project);
  317.     printf("Forward:    %s\n", forward);
  318.     printf("\n");
  319.     free(gecos);
  320.     return;
  321. }
  322.  
  323. #if 0
  324.  
  325. /*
  326.  *----------------------------------------------------------------------
  327.  *
  328.  * sendmail --
  329.  *
  330.  *    Send a mail message.
  331.  *
  332.  * Results:
  333.  *    None.
  334.  *
  335.  * Side effects:
  336.  *    Mail is sent.
  337.  *
  338.  *----------------------------------------------------------------------
  339.  */
  340. static void
  341. sendmsg(username)
  342.     CONST char *username;
  343. {
  344.     char buf[0x100];
  345.     CONST char *editor;
  346.     int tmp;
  347.  
  348.     if ((tmp = open(MAIL_TMP, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
  349.     fprintf(stderr, "Cannot open %s: %s\n", MAIL_TMP, strerror(errno));
  350.         return;
  351.     }
  352.     if (write(tmp, MAIL_MSG, strlen(MAIL_MSG)) != strlen(MAIL_MSG)) {
  353.     fprintf(stderr, "Error writing %s: %s\n", MAIL_TMP, strerror(errno));
  354.         return;
  355.     }
  356.     close(tmp);
  357.     if ((editor = getenv("EDITOR")) == 0) {
  358.     editor = "vi";
  359.     }
  360.     if (strcmp(editor, "mx") == 0) {
  361.     (void)sprintf(buf, "mx -D %s", MAIL_TMP);
  362.     } else {
  363.     (void)sprintf(buf, "%s %s", editor, MAIL_TMP);
  364.     }
  365.     if (system(buf)) {
  366.     fprintf(stderr, "Not sending mail: %s\n", strerror(errno));
  367.     } else if (yes("Do you want to mail the message?")) {
  368.     sprintf(buf, "mail %s -c %s -s \"Sprite account\" < %s",
  369.         username, whoami, MAIL_TMP);
  370.     if (system(buf)) {
  371.         fprintf(stderr, "Problem sending mail: %s\n", strerror(errno));
  372.     }
  373.     }
  374.     unlink(MAIL_TMP);
  375.     return;
  376. }
  377. #endif /* 0 */
  378.  
  379.  
  380. /*
  381.  *----------------------------------------------------------------------
  382.  *
  383.  * makeLinktoHomeDir --
  384.  *
  385.  *    Makes a link from /users/username to the real home directory.
  386.  *
  387.  * Results:
  388.  *    None.
  389.  *
  390.  * Side effects:
  391.  *    A symbolic link is created.
  392.  *
  393.  *----------------------------------------------------------------------
  394.  */
  395. static void
  396. makeLinktoHomeDir(homedir, username)
  397.     CONST char *homedir;
  398.     CONST char *username;
  399. {
  400.  
  401.     if (!strncmp(USER_DIR, homedir, strlen(USER_DIR))) {
  402.     return;
  403.     }
  404.     sprintf(sym_homedir, "%s/%s", USER_DIR, username);
  405.     if (symlink(homedir, sym_homedir)) {
  406.     if (errno != EEXIST) {
  407.         fprintf(stderr, "Cannot create link %s -> %s\n",
  408.         sym_homedir, homedir);
  409.     }
  410.     make_symlink = SKIP_THIS_STEP;
  411.     } else {
  412.     make_symlink = DONE;
  413.     }
  414.     return;
  415. }
  416.  
  417.  
  418. /*
  419.  *----------------------------------------------------------------------
  420.  *
  421.  * add2MailingList --
  422.  *
  423.  *    Adds a user to a mailing list.
  424.  *
  425.  * Results:
  426.  *    None.
  427.  *
  428.  * Side effects:
  429.  *    Modifies the sendmail aliases file.
  430.  *
  431.  *----------------------------------------------------------------------
  432.  */
  433. static void
  434. add2MailingList(list, name)
  435.     CONST char *list;
  436.     CONST char *name;
  437. {
  438.     char *expr;
  439.     int status;            /* exit status from subprocesses */
  440.     char logMsg[4096];        /* log message for "ci" */
  441.  
  442.     printf("Adding %s to \"%s\" mailing list\n", name, list);
  443.     sprintf(logMsg, "-madduser: add %s to \"%s\".", name, list);
  444.  
  445.     status = rcsCheckOut(ALIASES);
  446.     if (status < 0) {
  447.     cleanup(0);
  448.     /* NOTREACHED */
  449.     } else if (status != 0) {
  450.     goto error;
  451.     }
  452.  
  453.     /* 
  454.      * Edit a copy of the aliases file and then rename it to be the 
  455.      * real one. 
  456.      */
  457.     if (copy(ALIASES, ALIASES_TMP, 0644)) {
  458.     goto error;
  459.     }
  460.     expr = xmalloc(strlen(list) + strlen(name) + 20);
  461.     sprintf(expr, "/^%s:/s/, *$/, %s,/", list, name);
  462.     if (sed(ALIASES_TMP, expr, 0644)) {
  463.     fprintf(stderr, "Cannot change mail alias file\n");
  464.     free(expr);
  465.     goto error;
  466.     }
  467.     free(expr);
  468.  
  469.     /* XXX - error checking? */
  470.     unlink(ALIASES_BAK);
  471.     rename(ALIASES, ALIASES_BAK);
  472.     rename(ALIASES_TMP, ALIASES);
  473.  
  474.     /* 
  475.      * Check the aliases file back in.  If there's a problem, say that 
  476.      * we succeeded anyway (because we did edit the aliases).
  477.      */
  478.     status = rcsCheckIn(ALIASES, logMsg);
  479.     if (status == 0) {
  480.     printf("Added %s to %s mailing list.\n", name, list);
  481.     } else {
  482.     fprintf(stderr, "\nPlease check the aliases file.\n");
  483.     fprintf(stderr, "(Make sure that `%s' was added to `%s'\n",
  484.            name, list);
  485.     fprintf(stderr, "and that the file was checked in.)\n\n");
  486.     }
  487.     edit_mailaliases_file = DONE;
  488.     return;
  489.  
  490.  error:
  491.     fprintf(stderr, "\nWarning: unable to add `%s' to the list `%s'.\n",
  492.         name, list);
  493.     fprintf(stderr, "You'll have to edit the aliases file by hand.\n\n");
  494.     return;
  495. }
  496.  
  497.  
  498. /*
  499.  *----------------------------------------------------------------------
  500.  *
  501.  * sed --
  502.  *
  503.  *    Filter a file through sed.
  504.  *
  505.  * Results:
  506.  *    Returns the exit status from sed.  0 if successful,
  507.  *      non-zero if there were problems.
  508.  *
  509.  * Side effects:
  510.  *    File is changed, a backup file is created.
  511.  *
  512.  *----------------------------------------------------------------------
  513.  */
  514. static int
  515. sed(filename, expr, mode)
  516.     CONST char *filename;
  517.     CONST char *expr;
  518.     int mode;
  519. {
  520.     char tmp[MAXPATHLEN];
  521.     char old[MAXPATHLEN];
  522.     int child;
  523.     int w;
  524.     int in, out;
  525.     union wait ws;
  526.  
  527.     sprintf(tmp, "%s.adduser", filename);
  528.     sprintf(old, "%s.old", filename);
  529.     if ((in = open(filename, O_RDONLY)) < 0) {
  530.     fprintf(stderr, "Cannot open %s: %s\n", filename, strerror(errno));
  531.     return 1;
  532.     }
  533.     if ((out = open(tmp, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
  534.     fprintf(stderr, "Cannot open %s: %s\n", tmp, strerror(errno));
  535.         return 1;
  536.     }
  537.     switch (child = fork()) {
  538.  
  539.     case -1:
  540.     fprintf(stderr, "Fork failed: %s\n", strerror(errno));
  541.     cleanup(0);
  542.  
  543.     case 0:
  544.     dup2(in, 0);
  545.     dup2(out, 1);
  546.     execlp("sed", "sed", expr, NULL);
  547.     fprintf(stderr, "Cannot exec sed: %s\n", strerror(errno));
  548.     exit(EXIT_FAILURE);
  549.  
  550.     default:
  551.     while ((w = wait(&ws)) > 0 && w != child) {
  552.         continue;
  553.     }
  554.     if (ws.w_retcode) {
  555.         return ws.w_retcode;
  556.     }
  557.     break;
  558.     }
  559.     unlink(old);
  560.     if (rename(filename, old)) {
  561.     fprintf(stderr, "Cannot rename %s as %s: %s\n",
  562.         filename, old, strerror(errno));
  563.     return 1;
  564.     }
  565.     if (rename(tmp, filename)) {
  566.     fprintf(stderr, "Cannot rename %s as %s: %s\n",
  567.         tmp, filename, strerror(errno));
  568.     return 1;
  569.     }
  570.     unlink(old);
  571.     return 0;
  572. }
  573.  
  574.  
  575. /*
  576.  *----------------------------------------------------------------------
  577.  *
  578.  * createHomeDirectory --
  579.  *
  580.  *    Create a directory and copy the prototype new user directory
  581.  *      there.
  582.  *
  583.  * Results:
  584.  *    None.
  585.  *
  586.  * Side effects:
  587.  *    Creates directory.
  588.  *
  589.  *----------------------------------------------------------------------
  590.  */
  591. static void
  592. createHomeDirectory(dir, username, project, forward, uid, gid)
  593.     CONST char *dir;
  594.     CONST char *username;
  595.     CONST char *forward;
  596.     CONST char *project;
  597.     int uid;
  598.     int gid;
  599. {
  600.     int child;
  601.     int w;
  602.     char cshrc[MAXPATHLEN];
  603.     char expr[BUFFER_SIZE + 20];
  604.     char projf[MAXPATHLEN];
  605.     char forwf[MAXPATHLEN];
  606.     union wait ws;
  607.     int fd;
  608.  
  609.     printf("Creating home directory: %s\n", dir);
  610.     if (mkdir(dir, 0775) != 0) {
  611.     fprintf(stderr, "Cannot create %s: %s\n", dir, strerror(errno));
  612.     if (yes("Do you want to continue?")) {
  613.         create_home_dir  = SKIP_THIS_STEP;
  614.         return;
  615.     } else {
  616.         cleanup(0);
  617.     }
  618.     }
  619.     create_home_dir = DONE;
  620. #ifndef TEST
  621.     if (chown(dir, uid, gid)) {
  622.     fprintf(stderr, "Can't change ownership of %s: %s\n",
  623.         dir, strerror(errno));
  624.     cleanup(0);
  625.     }
  626. #endif
  627.     switch (child = fork()) {
  628.  
  629.     case -1:
  630.     fprintf(stderr, "Fork failed: %s\n", strerror(errno));
  631.     cleanup(0);
  632.  
  633.     case 0:
  634. #ifdef TEST
  635.     execlp("update", "update", NEWUSER_DIR, dir, NULL);
  636. #else
  637.     execlp("update", "update", "-o", username, NEWUSER_DIR, dir, NULL);
  638. #endif
  639.     fprintf(stderr, "Cannot exec update\n");
  640.     cleanup(0);
  641.  
  642.     default:
  643.     while ((w = wait(&ws)) > 0 && w != child) {
  644.         continue;
  645.     }
  646.     if (ws.w_retcode) {
  647.         if (yes("Update failed, do you want to continue?") == 0) {
  648.         cleanup(0);
  649.         }
  650.     }
  651.     break;
  652.     }
  653.     sprintf(cshrc, "%s/.cshrc", dir);
  654.     sprintf(expr, "s/newuser/%s/g", username);
  655.     if (sed(cshrc, expr, 0644)) {
  656.     fprintf(stderr, "Cannot edit %s\n", cshrc);
  657.     }
  658. #ifndef TEST
  659.     if (chown(cshrc, uid, gid)) {
  660.     fprintf(stderr, "Warning: Can't change ownership of %s: %s\n",
  661.         cshrc, strerror(errno));
  662.     }
  663. #endif
  664.     sprintf(projf, "%s/.project", dir);
  665.     if ((fd = open(projf, O_WRONLY|O_TRUNC|O_CREAT, 0644)) < 0) {
  666.     fprintf(stderr, "Cannot open %s: %s\n", projf, strerror(errno));
  667.     return;
  668.     }
  669.     strcat(project, "\n");
  670.     if (write(fd, project, strlen(project)) != strlen(project)) {
  671.     fprintf(stderr, "Error writing %s: %s\n", projf, strerror(errno));
  672.     }
  673.     close(fd);
  674.     sprintf(forwf, "%s/.forward", dir);
  675.     if (*forward) {
  676.     if ((fd = open(forwf, O_WRONLY|O_TRUNC|O_CREAT, 0644)) < 0) {
  677.         fprintf(stderr, "Cannot open %s: %s\n", forwf, strerror(errno));
  678.         return;
  679.     }
  680.     strcat(forward, "\n");
  681.     if (write(fd, forward, strlen(forward)) != strlen(forward)) {
  682.         fprintf(stderr, "Error writing %s: %s\n", forwf, strerror(errno));
  683.     }
  684.     } else {
  685.     unlink(forwf);
  686.     }
  687.     close(fd);
  688.     return;
  689. }
  690.  
  691.  
  692. /*
  693.  *----------------------------------------------------------------------
  694.  *
  695.  *  insertPasswdEntry --
  696.  *
  697.  *    Inserts a passwd entry into the password file.
  698.  *
  699.  * Results:
  700.  *    None.
  701.  *
  702.  * Side effects:
  703.  *    Modifies /etc/passwd.  Creates a backup file.
  704.  *
  705.  *----------------------------------------------------------------------
  706.  */
  707.  
  708. static void
  709. insertPasswdEntry(newpwd)
  710.     CONST struct passwd *newpwd;
  711. {
  712.     int fd;
  713.     FILE *ptmp;
  714.     struct passwd *pwd;
  715.     char buf1[16], buf2[16];
  716.  
  717.     if ((fd = open(PTMP_FILE, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) {
  718.     fprintf(stderr, "Cannot open %s: %s\n", PTMP_FILE, strerror(errno));
  719.     cleanup(0);
  720.     }
  721.     ptmp = fdopen(fd, "w");
  722. #ifndef TEST
  723.     if (getgrnam("wheel") == (struct group *)NULL) {
  724.     fprintf(stderr,"Warning: no wheel group\n");
  725.     } else {
  726.     fchown(fd, -1, getgrnam("wheel")->gr_gid);
  727.     }
  728. #endif
  729.  
  730.     create_ptmp_file = DONE;
  731.     while ((pwd = getpwent()) != NULL) {
  732.     if (strcmp(pwd->pw_name, newpwd->pw_name) == 0) {
  733.         fprintf(stderr,
  734.         "There is already an entry in /etc/passwd for %s\n",
  735.         newpwd->pw_name);
  736.         if (yes("Do you want to replace it?")) {
  737.         continue;
  738.         } else if (yes("Do you want to continue?")) {
  739.         unlink(PTMP_FILE);
  740.         create_ptmp_file = SKIP_THIS_STEP;
  741.         printf("%s left unchanged\n", MASTER_PASSWD_FILE);
  742.         return;
  743.         } else {
  744.         cleanup(0);
  745.         }
  746.     }
  747.     if (pwd->pw_change==0) {
  748.         *buf1 = '\0';
  749.     } else {
  750.         sprintf(buf1,"%l",pwd->pw_change);
  751.     }
  752.     if (pwd->pw_expire==0) {
  753.         *buf2 = '\0';
  754.     } else {
  755.         sprintf(buf2,"%l",pwd->pw_expire);
  756.     }
  757.     fprintf(ptmp, "%s:%s:%d:%d:%s:%s:%s:%s:%s:%s\n",
  758.         pwd->pw_name,
  759.         pwd->pw_passwd,
  760.         pwd->pw_uid,
  761.         pwd->pw_gid,
  762.         pwd->pw_class,
  763.         buf1,
  764.         buf2,
  765.         pwd->pw_gecos,
  766.         pwd->pw_dir,
  767.         pwd->pw_shell);
  768.     }
  769.     endpwent();
  770.     if (newpwd->pw_change==0) {
  771.     *buf1 = '\0';
  772.     } else {
  773.     sprintf(buf1,"%l",newpwd->pw_change);
  774.     }
  775.     if (newpwd->pw_expire==0) {
  776.     *buf2 = '\0';
  777.     } else {
  778.     sprintf(buf2,"%l",newpwd->pw_expire);
  779.     }
  780.     fprintf(ptmp, "%s:%s:%d:%d:%s:%s:%s:%s:%s:%s\n",
  781.     newpwd->pw_name,
  782.     newpwd->pw_passwd,
  783.     newpwd->pw_uid,
  784.     newpwd->pw_gid,
  785.     newpwd->pw_class,
  786.     buf1,
  787.     buf2,
  788.     newpwd->pw_gecos,
  789.     newpwd->pw_dir,
  790.     newpwd->pw_shell);
  791.      fclose(ptmp);
  792.      return;
  793. }
  794.  
  795.  
  796. /*
  797.  *----------------------------------------------------------------------
  798.  *
  799.  * getPasswdEntryFromUser --
  800.  *
  801.  *    Prompt the user for a string.
  802.  *
  803.  * Results:
  804.  *    Returns the string.
  805.  *
  806.  * Side effects:
  807.  *    None.
  808.  *
  809.  *----------------------------------------------------------------------
  810.  */
  811. static char *
  812. getPasswdEntryFromUser()
  813. {
  814.     static char buf[BUFFER_SIZE];
  815.  
  816.     getString("", "Enter the /etc/passwd line", buf);
  817.     return buf;
  818. }
  819.  
  820.  
  821. /*
  822.  *----------------------------------------------------------------------
  823.  *
  824.  * getPasswdEntryFromDataBase --
  825.  *
  826.  *    Gets a passwd file entry from the ucb global database.
  827.  *
  828.  * Results:
  829.  *    Returns then entry.
  830.  *
  831.  * Side effects:
  832.  *    None.
  833.  *
  834.  *----------------------------------------------------------------------
  835.  */
  836. static char *
  837. getPasswdEntryFromDataBase()    
  838. {
  839.     static char lastname[BUFFER_SIZE];
  840.     static char group[BUFFER_SIZE];
  841.     int child;
  842.     int pipefd[2];
  843.     int w;
  844.     static char entry[BUFFER_SIZE];
  845.     union wait ws;
  846.  
  847.     for (;;) {
  848.     getString(":", "Enter user's last name", lastname);
  849.     for (;;) {
  850.         getString(":", "Enter user's group", group);
  851.         if (getgrnam(group)) {
  852.         break;
  853.         }
  854.         fprintf(stderr,
  855.         "%s is not a valid group (not in /etc/group).\n", group);
  856.         fprintf(stderr, "Pleas try again\n");
  857.     }
  858.     printf("lastname is %s, group is %s\n", lastname, group);
  859.     if (yes("Is this correct?")) {
  860.         break;
  861.     }
  862.     }
  863.     printf("Fetching passwd entry from database on %s.\n", DATABASE_HOST);
  864.     printf("This will take a minute or two.  Please be patient....\n");
  865.     pipe(pipefd);
  866.     switch (child = fork()) {
  867.  
  868.     case -1:
  869.     fprintf(stderr, "Cannot fork: %s\n", strerror(errno));
  870.     exit(EXIT_FAILURE);
  871.  
  872.     case 0: /* child */
  873.         close(pipefd[0]);
  874.     dup2(pipefd[1], 1);
  875.     execlp("rsh", "rsh", "allspice", "-l", "root", "rsh", DATABASE_HOST,
  876.            "-l", "account", "=bin/mkpwent", USER_DIR, lastname, group,
  877.            NULL);
  878.     fprintf(stderr, "Cannot exec rsh: %s\n", strerror(errno));
  879.     exit(EXIT_FAILURE);
  880.  
  881.     default: /* parent */
  882.     close(pipefd[1]);
  883.         if ((w = read(pipefd[0], entry, sizeof(entry))) < 0) {
  884.         fprintf(stderr, "Read from pipe failed: %s\n", strerror(errno));
  885.         exit(EXIT_FAILURE);
  886.     }
  887.     if (entry[strlen(entry) - 1] == '\n') {
  888.         entry[strlen(entry) - 1] = '\0';
  889.     }
  890.     close(pipefd[0]);
  891.  
  892.     while ((w = wait(&ws)) > 0 && w != child) {
  893.         continue;
  894.     }
  895.     if (ws.w_retcode) {
  896.         fprintf(stderr, "Could't fetch entry from %s\n", DATABASE_HOST);
  897.         fprintf(stderr, "Make sure your machine is listed in /.rhosts\n");
  898.         cleanup(0);
  899.     }
  900.     break;
  901.     }
  902.     return entry;
  903. }
  904.  
  905.  
  906. /*
  907.  *----------------------------------------------------------------------
  908.  *
  909.  * getPasswdEntryInteractive --
  910.  *
  911.  *    description.
  912.  *
  913.  * Results:
  914.  *    None.
  915.  *
  916.  * Side effects:
  917.  *    None.
  918.  *
  919.  *----------------------------------------------------------------------
  920.  */
  921. static char *
  922. getPasswdEntryInteractive(pwd)
  923.     CONST struct passwd *pwd;
  924. {
  925.     static char username[BUFFER_SIZE];
  926.     static char userid[BUFFER_SIZE];
  927.     static char groupid[BUFFER_SIZE];
  928.     static char fullname[BUFFER_SIZE];
  929.     static char office[BUFFER_SIZE];
  930.     static char phone[BUFFER_SIZE];
  931.     static char home_phone[BUFFER_SIZE];
  932.     static char info[BUFFER_SIZE];
  933.     static char entry[BUFFER_SIZE];
  934.     static char passwd[BUFFER_SIZE];
  935.  
  936.     char *shell;
  937.     struct group *gp;
  938.  
  939.     if (pwd) {
  940.     char *gecos, *t1, *t2;
  941.  
  942.     strcpy(username, pwd->pw_name);
  943.     strcpy(passwd, pwd->pw_passwd);
  944.     sprintf(userid, "%d", pwd->pw_uid);
  945.     sprintf(groupid, "%d", pwd->pw_gid);
  946.     gecos = xmalloc(strlen(pwd->pw_gecos));
  947.     strcpy(gecos, pwd->pw_gecos);
  948.     t1 = parseGecos(gecos);
  949.     strcpy(fullname, gecos);
  950.     t2 = parseGecos(t1);
  951.     strcpy(office, t1);
  952.     t1 = parseGecos(t2);
  953.     strcpy(phone, t2);
  954.     strcpy(home_phone, t1);
  955.     }
  956.     getString(":", "new users login name", username);
  957.     getPasswd(passwd);
  958.     getNumber("user id", userid);
  959.     for (;;) {
  960.     getString(":", "group id", groupid);
  961.     if (isdigit(*groupid)) {
  962.         gp = getgrgid(atoi(groupid));
  963.     } else {
  964.         gp = getgrnam(groupid);
  965.     }
  966.     if (gp == NULL) {
  967.         fprintf(stderr, "%s is not a valid group\n", groupid);
  968.         fprintf(stderr, "Please try again\n");
  969.         continue;
  970.     }
  971.     break;
  972.     }
  973.     getString(":,", "full name", fullname);
  974.     getString(":,", "office", office);
  975.     getString(":,", "work phone number", phone);
  976.     getString(",:", "home phone number", home_phone);
  977.     shell = getShell();
  978.     sprintf(info, "%s,%s,%s,%s", fullname, office, phone, home_phone);
  979.     sprintf(entry, "%s:%s:%s:%d:%s:%s/%s:%s",
  980.         username, passwd, userid, gp->gr_gid, info, USER_DIR, username, shell);
  981.     return entry;
  982. }
  983.  
  984. static char *
  985. getHomeDir()
  986. {
  987.     static char dir[BUFFER_SIZE];
  988.     int c;
  989.  
  990.     printf("Where would you like to put the home directory?\n");
  991.     printf("Enter 1 for /user1    (misc accounts)\n");
  992.     printf("Enter 2 for /user6    (spriters)\n");
  993.     printf("Enter 3 for /user3    (on cory cluster)\n");
  994.     printf("Enter 4 for /user4    (raid hardware people)\n");
  995.     printf("Enter 5 for /pcs      (mic group)\n");
  996.     printf("Enter 6 for /postdev  (postgres people)\n");
  997.     printf("Enter 7 for somewhere else\n");
  998.     for (;;) {
  999.         printf("Please choose one of the above: ");
  1000.     c = raw_getchar();
  1001.     printf("\n");
  1002.     switch (c) {
  1003.  
  1004.     case '1':
  1005.         strcpy(dir, "/user1");
  1006.         break;
  1007.  
  1008.     case '2': 
  1009.         strcpy(dir, "/user6");
  1010.         break;
  1011.  
  1012.     case '3':
  1013.         strcpy(dir, "/user3");
  1014.         break;
  1015.  
  1016.     case '4':
  1017.         strcpy(dir, "/user4");
  1018.         break;
  1019.  
  1020.     case '5':
  1021.         strcpy(dir, "/pcs");
  1022.         break;
  1023.  
  1024.     case '6':
  1025.         strcpy(dir, "/postdev");
  1026.         break;
  1027.  
  1028.     case '7':
  1029.         for (;;) {
  1030.         getString("*?[]", "Please enter the directory", dir);
  1031.         if (*dir != '/') {
  1032.             fprintf(stderr, "Path must start at root.");
  1033.             continue;
  1034.         }
  1035.         if (strlen(dir) > 1 && dir[strlen(dir) - 1] == '/') {
  1036.             dir[strlen(dir) - 1] = '\0';
  1037.         }
  1038.         break;
  1039.         }
  1040.         break;
  1041.  
  1042.     default:
  1043.         continue;
  1044.     }
  1045.     break;
  1046.     }
  1047.     return dir;
  1048. }
  1049.  
  1050. static char *
  1051. pwskip(p)
  1052.     char *p;
  1053. {
  1054.     while (*p && *p != ':') {
  1055.     ++p;
  1056.     }
  1057.     if (*p) {
  1058.     *p++ = 0;
  1059.     }
  1060.     return p;
  1061. }
  1062.  
  1063. static int
  1064. parsePasswdEntry(p, ps)
  1065.     char *p;
  1066.     struct passwd *ps;
  1067. {
  1068.     char *uid, *gid;
  1069.     static char homedir[MAXPATHLEN];
  1070.  
  1071.     ps->pw_name = p;
  1072.     p = pwskip(p);
  1073.     ps->pw_passwd = p;
  1074.     uid = p = pwskip(p);
  1075.     ps->pw_uid = atoi(p);
  1076.     gid = p = pwskip(p);
  1077.     ps->pw_gid = atoi(p);
  1078.     p = pwskip(p);
  1079.     ps->pw_class = "";
  1080.     ps->pw_change = 0;
  1081.     ps->pw_expire = 0;
  1082.     ps->pw_gecos = p;
  1083.     p = pwskip(p);
  1084.     ps->pw_dir = p;
  1085.     p = pwskip(p);
  1086.     ps->pw_shell = p;
  1087.     if (strlen(ps->pw_name) == 0) {
  1088.     fprintf(stderr, "Null username\n");
  1089.     return 0;
  1090.     }
  1091.     if (*ps->pw_passwd != '*' && strlen(ps->pw_passwd) != 13) {
  1092.     fprintf(stderr, "Invalid encrypted password: \"%s\"\n", ps->pw_passwd);
  1093.     return 0;
  1094.     }
  1095. #if 0    
  1096.     while (strlen(ps->pw_passwd) != 13) {
  1097.     if (*ps->pw_passwd == '\0' || strcmp(ps->pw_passwd, "*") == 0) {
  1098.         printf("Please enter a password\n");
  1099.         ps->pw_passwd = getPasswd();
  1100.         continue;
  1101.     }
  1102.     fprintf(stderr, "Invalid encrypted passwd\n");
  1103.     return 0;
  1104.     }
  1105. #endif
  1106.     if (checkNumber(uid) == 0) {
  1107.     fprintf(stderr, "Non digit in uid field\n");
  1108.     return 0;
  1109.     }
  1110.     if (checkNumber(gid) == 0) {
  1111.     fprintf(stderr, "Non digit in gid field\n");
  1112.     return 0;
  1113.     }
  1114.     sprintf(homedir, "%s/%s", USER_DIR, ps->pw_name);
  1115.     ps->pw_dir = homedir;
  1116.     if (*ps->pw_shell != '/') {
  1117.     fprintf(stderr, "Bad shell: %s\n", ps->pw_shell);
  1118.     return 0;
  1119.     }
  1120.     return 1;
  1121. }
  1122.  
  1123. static char *
  1124. xmalloc(n)
  1125.     int n;
  1126. {
  1127.     char *p;
  1128.  
  1129.     if ((p = malloc(n)) == NULL) {
  1130.     fprintf(stderr, "Malloc failed: %s\n", strerror(errno));
  1131.     cleanup(0);
  1132.     }
  1133.     return p;
  1134. }
  1135.  
  1136.  
  1137. /*
  1138.  *----------------------------------------------------------------------
  1139.  *
  1140.  * copy --
  1141.  *
  1142.  *    Create a copy of a file.
  1143.  *
  1144.  * Results:
  1145.  *    Returns 0 if there was no error, non-zero if there was an error.
  1146.  *
  1147.  * Side effects:
  1148.  *    None.
  1149.  *
  1150.  *----------------------------------------------------------------------
  1151.  */
  1152.  
  1153. static int
  1154. copy(from, to, mode)
  1155.     CONST char *from;
  1156.     CONST char *to;
  1157.     int mode;            /* the permissions for the new file */
  1158. {
  1159.     char *buf;
  1160.     int in, out;
  1161.     long in_len;
  1162.  
  1163.     if ((in = open(from, O_RDONLY)) < 0) {
  1164.     fprintf(stderr, "Cannot open %s: %s\n", from, strerror(errno));
  1165.     return 1;
  1166.     }
  1167.     if ((out = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
  1168.     fprintf(stderr, "Cannot open %s: %s\n", to, strerror(errno));
  1169.     return 1;
  1170.     }
  1171.     in_len = lseek(in, 0, L_XTND);
  1172.     lseek(in, 0, L_SET);
  1173.     buf = xmalloc(in_len);
  1174.     if (read(in, buf, in_len) != in_len) {
  1175.     fprintf(stderr, "Error reading %s: %s\n", to, strerror(errno));
  1176.     return 1;
  1177.     }
  1178.     close(in);
  1179.     if (write(out, buf, in_len) != in_len) {
  1180.     fprintf(stderr, "Error writing %s: %s\n", to, strerror(errno));
  1181.     return 1;
  1182.     }
  1183.     free(buf);
  1184.     return 0;
  1185. }
  1186.  
  1187. static void    
  1188. cleanup(sig)
  1189.     int sig;            /* signal number (ignored) */
  1190. {
  1191.     fprintf(stderr, "\nCleaning up ...\n");
  1192.     if (move_ptmp_to_passwd == DONE) {
  1193.     if (rename(MASTER_PASSWD_FILE, PTMP_FILE)) {
  1194.         fprintf(stderr, "Cannot rename %s to %s: %s\n",
  1195.         MASTER_PASSWD_FILE, PTMP_FILE, strerror(errno));
  1196.     } else if (rename(MASTER_BAK, MASTER_PASSWD_FILE)) {
  1197.         fprintf(stderr, "Cannot rename %s to %s: %s\n",
  1198.         MASTER_BAK, MASTER_PASSWD_FILE, strerror(errno));
  1199.     } else if (rename(PASSWD_BAK, PASSWD_FILE)) {
  1200.         fprintf(stderr, "Cannot rename %s to %s: %s\n",
  1201.         PASSWD_BAK, PASSWD_FILE, strerror(errno));
  1202.     } else {
  1203.         unlink(PTMP_FILE);
  1204.     }
  1205.     } else if (create_ptmp_file == DONE) {
  1206.     if (unlink(PTMP_FILE)) {
  1207.         fprintf(stderr, "Cannot unlink %s: %s\n", PTMP_FILE,
  1208.             strerror(errno));
  1209.     }
  1210.     }
  1211.     if (edit_mailaliases_file == DONE) {
  1212.     if (unlink(ALIASES_TMP)) {
  1213.         fprintf(stderr, "Cannot unlink %s: %s\n",
  1214.         ALIASES_TMP, strerror(errno));
  1215.     }
  1216.     }
  1217.     if (create_home_dir == DONE) {
  1218.     fprintf(stderr, "Deleteing %s/* ...\n", real_homedir);
  1219.     if (fork() == 0) {
  1220.         execlp("rm", "rm", "-rf", real_homedir, NULL);
  1221.     }
  1222.     }
  1223.     if (make_symlink == DONE) {
  1224.     if (unlink(sym_homedir)) {
  1225.         fprintf(stderr, "Cannot unlink %s: %s\n",
  1226.         sym_homedir, strerror(errno));
  1227.     }
  1228.     }
  1229.     exit(EXIT_FAILURE);
  1230. }
  1231.  
  1232.